/**
 * @license
*
* PreProcessing is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PreProcessing is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PreProcessing.  If not, see <http://www.gnu.org/licenses/>.
* 
*/


/**
 * @file FFTBean.java 
 * 
 * @brief This java-bean takes in an audio file which then transforms its 
 * signal to Fast Fourier Transform and sets the newly transformed data 
 * into an array.
 * 
 * @author Quintin Maseyk (ID: 10185451)
 */

package FFTAnalysis;

import flanagan.math.FourierTransform;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.io.Serializable;


/*
 * @class FFTBean contains a constructor which transforms an audio wave file 
 * by Flanagan's FFT and setting the outcome in a Complex data array.
 */
public class FFTBean implements Serializable 
{  
    // PREPARE inputPath | holds the [full path] in which a converted audio file is located    
    private String inputPath; 
    
    // PAREPARE audio file IN | holds the the converted audio [file]    
    private File in; 

    // PREPARE component-based java-bean listener
    private PropertyChangeSupport propertySupport; 
    
    
    /*
     * @brief FFTBean() default constructor
     * Sets up the input file which gets transformed and 
     * Sets up the event handler which listens for NormaliseBean to POST
     */
    public FFTBean() 
    {
        inputPath = "";
        in = null;
        propertySupport = new PropertyChangeSupport(this);
    }
    
    
    /*
     * @brief ADD listener which awaits NormaliseBean's outputFile to be POST
     */
    public void addPropertyChangeListener(PropertyChangeListener listener) 
    {
        propertySupport.addPropertyChangeListener(listener);
    }
    
    
    /*
     * @brief REMOVE listener
     */
    public void removePropertyChangeListener(PropertyChangeListener listener) 
    {
        propertySupport.removePropertyChangeListener(listener);
    }
    
    
    /**
     * @brief GET inputPath from listener via NormaliseBean
     * @param newInputPath the input path of [this] audio file to be transformed
     */
    public void setInputPath(String newInputPath) 
    {
        inputPath = newInputPath;
        this.doFFT();
    }

    
    /*
     * @brief Transform converted audio file to FFT. 
     * Send FFT data to Interface: Learning whilst iterating through FFT data array. 
     * 
     * @ref http://www.labbookpages.co.uk/audio/javaWavFiles.html  
     */
    public void doFFT()
    {
        try
        {
        // RETRIEVED FROM setInputPath via NormaliseBean
            in = new File(inputPath);

            
        // START OF FFTBean
            System.out.println("\n--------------------------------------" +
                            "\nFFTBean [start]" + 
                            "\n--------------------------------------");   


        // Open the wav file specified as the first argument
            WavFile wavFile = WavFile.openWavFile(in);


        // CREATE A BUFFER OF 2048
        // 1 = NUMBER OF CHANNELS [mono]
            double[] buffer = new double[2048 * 1];


        // PREPARE READ VARIABLES
            int framesRead = 0;
            double min = Double.MAX_VALUE;
            double max = Double.MIN_VALUE;


        // SETUP FOURIER TRANSFORM: ft
            FourierTransform ft = new FourierTransform();


        // WHILE BUFFER IS NOT COMPLETE -READ IN EACH FRAME
        // IF FRAME IS < MIN || > MAX ASSIGN BUFFER TO ft DATA ARRAY
            do
            {
                // READ FRAMES INTO BUFFER
                framesRead = wavFile.readFrames(buffer, 2048);

                // LOOP THROUGH FRAMES AND LOOK FOR MINIMUM AND MAXIMUM VALUE
                for (int s=0 ; s<framesRead * 1 ; s++)
                {
                    // MAKE 2 DECIMAL PLACES
                    if (buffer[s] > max) {max = Math.round(buffer[s]*Math.pow(10,2))/Math.pow(10,2);}
                    if (buffer[s] < min) {min = Math.round(buffer[s]*Math.pow(10,2))/Math.pow(10,2);}                        

                    // For each iteration assign [this] value to FFT Double Array
                    ft.setData(buffer);
                }
            }
            while (framesRead != 0);      


        // TRANSFORM NEWLY CAPTURED ft DATA
            ft.transform();   


        // CREATE NEW DOUBLE ARRAY CONTAINING ft DATA
            double[] tData = null;
            tData = ft.getTransformedDataAsAlternate();


        // DISPLAY ALL ft DATA
            for (int i=0; i<ft.getUsedDataLength(); i++)
            {
                System.out.println(tData[i]);
            }
        
            
        // WHEN BUFFER CHANGES TO tDATA, TRIGGER EVENT 
        // [Learning Component's listener is to catch this]
            propertySupport.firePropertyChange("", buffer, tData);


        /*
        * This method sets the value of the sampling period. 
        * If this is set, it is used by the graphing and printing 
        * methods to convert the cycles per unit grid point to a true frequency.
        */
            ft.setDeltaT(128);


        /*
        * The number of the segments used in powerSpectrum is set by this 
        * method. If the length of the segment and hence the number of 
        * segments have already been set by calling setSegmentLength that 
        * calculated number will be overwritten by this later call to 
        * setSegmentNumber. If neither setSegmentNumber nor setSegmentLength 
        * are called the default value of the segment number is one and the 
        * default value of the segment length is the full data length.
        */
            ft.setSegmentNumber(2);


        // SET ft WINDOWING FUNCTION   
            ft.setHann();


        // PLOT ft SPECTRUM ON GRAPH
            //ft.plotPowerSpectrum("TEST");


        // PLOT ft POWER LOG ON GRAPH
            ft.plotPowerLog();


        // Close the wavFile
            wavFile.close();


        // Output the minimum and maximum value
            //System.out.printf("Min: %f, Max: %f\n", min, max);    


        // END OF FFTBean
            System.out.println("\n--------------------------------------" +
                            "\nFFTBean [end]" + 
                            "\n--------------------------------------");
        }
        catch (Exception e)
        {
            System.err.println(e);
        }
    }
}